Skip to content

Merge dev into main: kap-server steer flag & agent-core-v2 full compaction - #3708

Open
sailist wants to merge 14 commits into
mainfrom
dev
Open

Merge dev into main: kap-server steer flag & agent-core-v2 full compaction#3708
sailist wants to merge 14 commits into
mainfrom
dev

Conversation

@sailist

@sailist sailist commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Changes

@changeset-bot

ghost commented Sep 10, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: caf839a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new

ghost commented Sep 10, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@bc67e87
npx https://pkg.pr.new/@moonshot-ai/kimi-code@bc67e87

commit: bc67e87

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1506063502

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

nextTurnId: state.turnIndex.nextTurnId,
branch: store.ref.branch,
head: deps.stores.tree.openBranch(store.ref.branch).head,
tokensBefore: estimateUsedContextTokens(state.history),

ghost Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use the same token basis across compaction rounds

When usage metadata is unavailable and the system prompt or tool definitions are large, onBeforeStep calculates usage including those prefixes, while this snapshot records tokensBefore from history alone. The controller later assigns this smaller value to lastCompactedTokens, so if the fixed prefix still puts the compacted context above the threshold, the continuation immediately triggers another compaction; every round repeats the mismatch and can loop through paid summary requests without reaching the normal LLM call. Record and compare token counts using the same prefix-aware calculation.

Useful? React with 👍 / 👎.

Comment on lines +461 to +464
({ context }) => {
if (context.cause !== 'user-abort') {
deps.actor.send({ type: 'input.continue' });
}

ghost Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resume the blocked prompt when auto-compaction fails

When budget compaction is triggered before a prompt's first LLM step and summarization then fails, the before-step hook has already ended that turn without producing a response. This cancellation path sends only input.continue, but the idle agent resumes on that event only when history ends mid-tool-chain; a history ending with the triggering user message therefore stays idle, while the prompt has already settled as successfully completed with no answer. Auto-compaction failure needs to requeue a continuation or otherwise restart the blocked turn.

Useful? React with 👍 / 👎.

Comment on lines +239 to +242
return {
history: state.history,
queue: state.queue,
nextTurnId: state.turnIndex.nextTurnId,

ghost Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve pending reminders when switching branches

An idle agent may legitimately have persisted input.remind entries waiting for its next turn, but the quiesce snapshot captures only history and queue. The fresh compaction branch therefore initializes reminders as empty, and replay starts after this snapshot's head, so any reminder already present before compaction is silently discarded and never delivered. Include the current reminder state in the seed, along with any other pending input state that must survive the reset.

Useful? React with 👍 / 👎.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 619717435b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +495 to +497
function defaultContinuation(reason: CompactionReason): UserMessage | undefined {
if (reason === 'manual') return undefined;
return compactionContinuationMessage();

ghost Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resume work interrupted by manual compaction

When manual compaction begins while a turn is executing tools, the pause ends that turn at the next tool-result boundary, and branch switching replaces the mid-tool-chain history with user messages plus the summary. Returning no continuation for every manual run then leaves the reset agent idle, so the original prompt is reported as done without ever producing its final response. Enqueue a continuation when a manual compaction actually interrupted a running turn.

Useful? React with 👍 / 👎.

Comment on lines +230 to +233
deps.actor.send({ type: 'input.pause' });
const waiting = waitFor(deps.actor, (s) => s.matches('idle'), { timeout: PAUSE_TIMEOUT_MS });
void waiting.catch(() => undefined);
await Promise.race([waiting, aborted(signal)]);

ghost Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Wait for detached tools before switching branches

When the agent has detached background tools, its state is idle.waiting, which still satisfies this broad matches('idle') predicate. Compaction therefore proceeds to store.reset; the agent reset aborts its scope and clears background, silently terminating those tools and discarding their eventual completion notifications. Quiescing should wait for an idle state with no background work, or explicitly preserve the detached tasks across the reset.

Useful? React with 👍 / 👎.

goal_control: z.enum(['pause', 'resume', 'cancel']).optional(),
disabled_tools: z.array(z.string()).optional(),
prompt_id: z.string().min(1).optional(),
steer: z.boolean().optional(),

ghost Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add a changeset for the new steer behavior

This adds a documented, user-visible prompt submission capability, but the reviewed diff contains no .changeset/ entry, so the CLI release changelog and versioning will omit it. Add the required user-facing changeset before merging.

AGENTS.md reference: AGENTS.md:L86-L87

Useful? React with 👍 / 👎.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1814aa78d5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +211 to +213
deps.actor.on('turn.aborting', () => {
active?.actor.send({ type: 'cancel', cause: 'user-abort' });
}),

ghost Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Route idle aborts to the active compaction

Once budget compaction has quiesced the agent, the agent is paused in idle, where input.abort has no handler and therefore emits no turn.aborting; this subscription is consequently unable to cancel the active summarization. An abort issued during that potentially long or paid phase does nothing, and a successful automatic compaction subsequently submits its continuation and restarts work the user tried to stop. Ensure aborts reach the active compaction even when no turn is running.

Useful? React with 👍 / 👎.

Comment on lines +150 to +152
const seedStore = await createEventStore({ journal, slices: agentSlices });
try {
await seedStore.dispatch([...opts.seed]);

ghost Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve registered slice state across the branch switch

When the original agent store has a registered stateful slice such as todoSlice, this fresh branch is populated only with the fixed agentSlices and the compaction seed's history/queue events. Resetting the original store then refolds its registered todo slice over that branch without any state.updated event, so a nonempty structured TodoList becomes empty; appending rendered todo text to the LLM summary does not restore what the TodoList tool or UI reads. Seed the current registered state, including the todo snapshot, before resetting.

Useful? React with 👍 / 👎.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5f639d6e62

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

compactionStarted(): boolean {
if (this.compacting) return false;
this.compacting = true;
if (this.status !== 'running' || this.turn === undefined) return false;

ghost Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Publish manual compaction as an agent turn

When POST /sessions/{id}:compact starts manual compaction, AgentFullCompactionService.begin requires the loop to be idle, so this tracker normally has status === 'idle' and no turn. This guard therefore suppresses the agent.state update, and because this change also removes compacting from session.state, clients receive no indication that the asynchronous manual compaction is running. Transition idle agents to a running { status: 'compacting' } turn here as well.

Useful? React with 👍 / 👎.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: be69ace220

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

index: number,
member: SwarmMemberResult | undefined,
): void => {
if (tasks.has(agentId)) return;

ghost Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh repeated swarm member tasks

When a later AgentSwarm call resumes an agent that appeared in an earlier call, tasks already contains that agent ID, so this return discards the later run's status, result, and timeline placement. Cold history consequently keeps showing the first run even though the live projector has updated the member; update the existing task or key synthesized tasks by both tool call and agent.

Useful? React with 👍 / 👎.

Comment on lines +201 to +205
deps.actor.on('turn.done', () => {
overflowAttempts = 0;
}),
deps.actor.on('turn.failed', (event) => {
if (!isContextOverflowError(event.error) || overflowAttempts >= maxAutoAttempts) {

ghost Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reset overflow retries after aborted turns

After an overflow triggers compaction, overflowAttempts is reset only by turn.done. If the resumed turn is aborted, the next unrelated prompt inherits the previous attempt count; at the configured cap its first context-overflow failure is no longer compacted and is surfaced immediately. Reset the counter on aborted turns and on terminal non-overflow failures while preserving it only across the same overflow recovery chain.

Useful? React with 👍 / 👎.

const current = { actor, reason, startedAt: Date.now() };
active = current;
const subscriptions = pipeEvents(actor);
await deps.stores.session();

ghost Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear active compaction when session-store setup fails

If opening the session store rejects because of an I/O or backend failure, this await runs before the try/finally, after active and the event subscriptions have already been installed. The caller receives the error, but every subsequent manual compaction reports busy and the subscriptions leak until the controller is recreated. Include setup and actor startup in the cleanup-protected region.

Useful? React with 👍 / 👎.

function parseSwarmMembers(output: string): SwarmMemberResult[] {
if (!output.includes('<agent_swarm_result>')) return [];
const members: SwarmMemberResult[] = [];
for (const match of output.matchAll(/<subagent\b([^>]*)>([\s\S]*?)<\/subagent>/g)) {

ghost Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse swarm results without treating report text as markup

When a subagent report contains the literal text </subagent>—for example while discussing this result format—the producer inserts the report body without escaping it, so this non-greedy regex closes at that text and stores a truncated result_summary or error in cold history. Recover member results from structured events or use an encoding that cannot conflict with arbitrary report content.

Useful? React with 👍 / 👎.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9329c09edb

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const isV3 = url === WS_PATH_V3 || url.startsWith(`${WS_PATH_V3}?`);
const isDebug = url === WS_DEBUG_PATH || url.startsWith(`${WS_DEBUG_PATH}?`);
const wss = isV1 ? wssV1 : isV3 ? wssV3 : isDebug ? wssDebug : undefined;
const wss = isV3 ? wssV3 : isDebug ? wssDebug : undefined;

ghost Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sync the shipped web bundle with the v3-only server

At this commit, the committed apps/kimi-code/dist-web/assets/index-HU0LCM-X.js still constructs /api/v1/ws, contains no /api/v3/ws client, and calls the removed transcript/message REST endpoints, while this branch now accepts only v3/debug WebSocket upgrades and removes those REST registrations. Consequently, the web UI shipped by kimi web cannot establish its chat socket or load history; sync and commit the code-app bundle together with the protocol removal.

AGENTS.md reference: AGENTS.md:L18-L18

Useful? React with 👍 / 👎.

Comment on lines +63 to +64
if (getLiveSessionById(this.deps.core.accessor, sessionId) !== undefined) {
return existing.view;

ghost Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh the cached roster for live session searches

When a live session is searched once and later creates a new subagent, this returns the existing view whose roster was populated only once by loadRoster. Session-wide search derives its agent IDs from view.agents(), so it never calls ensureAgentHistory for the new subagent and silently omits that agent's messages from all subsequent searches until the cached source is dropped; refresh the roster or subscribe to agent lifecycle changes.

Useful? React with 👍 / 👎.

Comment on lines +152 to +154
const cached = this.sessions.get(sessionId);
this.sessions.delete(sessionId);
if (cached === undefined) return [];

ghost Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Emit deletions for sessions absent from the event cache

Deleting a cold session, or any session that has not emitted a created/meta/activity event since this translator started, always hits this empty-cache return. The session has already been removed from ISessionIndex, so it cannot be fetched afterward, and v3 clients receive no deletion message; for example, kimi-inspect retains its seeded session entry until a later poll or unrelated invalidation. Seed the cache from existing sessions or construct a deletion entity from lifecycle data.

Useful? React with 👍 / 👎.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fde8ccef73

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +172 to +173
function replaceEntryText(entry: UserEntry, text: string): UserEntry {
return { ...entry, message: { ...entry.message, content: [{ type: 'text', text }] } };

ghost Sep 11, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve media when truncating retained user messages

When a boundary user message exceeds the head/tail token budget and contains images, audio, or video alongside text, this replacement rebuilds its content from a single text part and silently discards every non-text part. For example, a recent prompt containing several screenshots can cross the 20k compaction budget and lose all screenshots from the compacted branch, leaving the continued turn unable to inspect the original inputs. Preserve the non-text parts on one retained fragment while accounting for their token cost.

Useful? React with 👍 / 👎.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0d48c459aa

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

agentId: file.agentId,
role: e.role,
text: e.text.length > MAX_DOC_TEXT_CHARS ? e.text.slice(0, MAX_DOC_TEXT_CHARS) : e.text,
text: e.text,

ghost Sep 11, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore the per-message cap before indexing wire text

When a user or assistant message exceeds 20,000 characters and the only match occurs in its suffix, the persistent route now indexes the entire e.text, while collectLiveDocs in searchService.ts still truncates the same message to MAX_DOC_TEXT_CHARS. Consequently, an identical session-scoped search can return no hit while the session is live and then return a hit after it becomes cold; retaining the existing cap here also prevents exceptionally large prompts from unnecessarily inflating the persisted text indexes.

Useful? React with 👍 / 👎.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 84645fc45d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

if (dir === undefined) return [];
let text: string;
try {
text = await readFile(join(dir, 'agents', agentId, 'wire.jsonl'), 'utf-8');

ghost Sep 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stream live wire history instead of reading it wholesale

For any session-scoped search against a live, long-running session, this loads the entire append-only wire.jsonl into one string and then split('\n') parses every record before the search deadline or text budget is established. Since wire history retains old turns and can contain very large tool-result records that produce no searchable document, a single search can consume memory proportional to the full session log, block far beyond the configured query deadline, or exhaust the server heap. Read and fold the file incrementally, or cache an incrementally updated bounded projection.

Useful? React with 👍 / 👎.

…story fold (#3754)

Fork copies the source agent's context into the child wire as bare
append_message records with no turn structure. The history fold
synthesized a numbered turn for them, shifting every real turn off its
engine turnId: answers landed in the wrong turn and the last turn never
saw its turn.ended, so /history reported a running turn and missing
turns for forked (btw) agents.

Write a durable agent.fork record after the seed copy, fold
pre-boundary seed messages into an unnumbered t-1 turn instead of
consuming engine turn ids, and expose the boundary as a fork.boundary
system message so clients can filter the inherited prefix.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6013b9eb52

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

logger,
});
const wssDebug = debugEndpoints ? registerWsDebug() : undefined;
const wssDebug = registerWsDebug();

ghost Sep 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the debug socket behind --debug-endpoints

When the server starts without --debug-endpoints, or uses a LAN/public bind where debugEndpoints is forced off, this still creates the debug WebSocket and the upgrade handler always routes /api/v1/debug/ws to it. Consequently, any bearer-authenticated client can subscribe to raw XState inspection traffic even though the HTTP debug surface is disabled; restore conditional registration and routing so the flag and loopback restriction cover the entire debug surface.

AGENTS.md reference: AGENTS.md:L27-L27

Useful? React with 👍 / 👎.

Comment on lines +97 to +98
const manager = core.accessor.get(ISessionManager);
const closeDisposable = manager.onDidCloseSession?.((event) => drop(event.sessionId));

ghost Sep 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Attach activity watchers when sessions become live

When an existing session is resumed and work starts without first publishing an app-level IEventService event—for example, by materializing the session and submitting through the debug Service RPC—this map never attaches an ISessionActivityView listener because it watches only global events and session closure, not ISessionManager.onDidCreateSession. Subsequent activity changes therefore produce no global session update, leaving established kimi-inspect clients with a stale idle badge until reconnect; attach on session creation/resume and seed any already-live sessions.

AGENTS.md reference: apps/kimi-inspect/AGENTS.md:L26-L28

Useful? React with 👍 / 👎.

turns.set(turnId, draft);
order.push(`turn:${turnId}`);
timelineIds.push(turnId);
rawId = seedEnded ? nextTurnId : SEED_TURN_RAW_ID;

ghost Sep 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make the seed turn removable by clear

When a forked agent has inherited assistant messages, this creates a t-1 turn, but turnOrdinalOf recognizes only t followed by digits. A later context.clear consequently classifies t-1 as a system ID and removes neither the turn:t-1 order entry nor its child entities, so cold GET .../history rebuilds the inherited pre-clear context after reload even though the live projection cleared it. Use an ID recognized as a turn or handle this sentinel explicitly in removal logic.

Useful? React with 👍 / 👎.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 35ca4a144a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

},
},
});
{ steerIfActive: req.body.steer === true },

ghost Sep 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Materialize media before completing a direct steer

When steer: true is submitted during an active turn with an image or video supplied by a server-local path, prompt preparation stores the media in an owned daemon file. This direct-steer path resolves handle.launched as soon as the entry is steered but, unlike the ordinary prompt gate and explicit steer endpoint, never materializes that daemon reference; the existing launch cleanup then deletes the file before the next LLM step, causing the media resolver to substitute unavailable-media text. Materialize daemon references before steering or delay cleanup until the media has been copied.

Useful? React with 👍 / 👎.

Comment on lines +1494 to +1497
: outcome === undefined
? tool.status === 'done'
? 'completed'
: 'failed'

ghost Sep 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep active resumed swarm members in the running state

When live history is requested while an AgentSwarm call with resume_agent_ids is still executing, there is no member outcome yet and tool.status is running, but this branch synthesizes every known resumed member as failed and later labels it interrupted. REST history consumers therefore show agents as failed while they are still working; use running for a live running tool and reserve interrupted/failed states for cold or terminal calls.

Useful? React with 👍 / 👎.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6cfa82828e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +11 to +14
export class AgentFork extends AgentEvent2<z.infer<typeof agentForkSchema>> {
static override readonly type = 'agent.fork';
static override readonly durable = true;
static override readonly schema = agentForkSchema;

ghost Sep 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Register the durable fork event before restoring it

When any forked agent is reopened, its persisted agent.fork record is absent from both EVENT2_REGISTRY and all state-fold contributions, so EventDispatcherService.replayRecords treats it as an unknown wire record and emits a corruption-style warning on every restore. Register AgentFork with registerEvent2Class (or contribute it explicitly) so this newly written durable record is recognized.

Useful? React with 👍 / 👎.

… let cron prompts through the display gate (#3761)

* refactor(agent-core-v2): let cron origins through the prompt display gate and add text-part contentType

* refactor(agent-core-v2): build envelope text parts through the structural history message builder

* refactor(kap-server,kimi-code): propagate text-part contentType to the wire and filter envelopes by it in the tui

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc67e876fd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

return INTERNAL_ORIGINS.has(origin.kind);
if (INTERNAL_ORIGINS.has(origin.kind)) return true;
if (origin.kind === 'user') return false;
return msg.content.some((part) => part.type === 'text' && part.contentType === 'text/xml');

ghost Sep 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep slash-skill turns in exported transcripts

When a user invokes a standalone slash skill, AgentSkillService.activate now stores its skill_activation prompt as text/xml; because its origin is not user, this condition classifies the entire user message as internal. groupIntoTurns consequently drops the skill invocation and groups its assistant response into the preceding visible turn (or creates an assistant-only first turn), so exported Markdown loses both the command and the correct conversation boundaries. Preserve a sanitized skill-activation boundary, such as /<skill> <args>, rather than filtering the whole message.

Useful? React with 👍 / 👎.

.filter((part): part is TextPromptPart => part.type === 'text' && part.contentType === 'text/xml')
.map((part) => part.text)
.join('');
if (envelope.length === 0) return contentPartsToText(content);

ghost Sep 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse cron envelopes that lack content metadata

For sessions containing cron records persisted before contentType was introduced, the text part has no text/xml marker, so this fallback returns the complete legacy <cron-fire ...> envelope. The previous replay path extracted <prompt> or stripped the outer envelope, meaning reopening an existing session after this change exposes raw protocol XML instead of the scheduled prompt or missed-task text. Retain the legacy envelope parser when no typed XML part is present.

Useful? React with 👍 / 👎.

…call id (#3736)

Remove the generated task-id layer from AgentTaskService: a background task
is now identified by the tool call id of the Bash/Agent/AskUserQuestion call
that started it, instead of a separately minted id. Field and parameter names
stay taskId/task_id across the wire protocols (kap-server v3 messages/REST,
klient), the TUI, vis, and kimi-inspect — only the value changes — so external
consumers are unaffected. Persistence files are named by the task id value,
duplicate registration of the same id is rejected as a bug, and the legacy
on-disk migration along with the unused track()/ITaskService injection is
removed.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2c6dc12153

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@@ -153,8 +164,18 @@ export class SessionActivityHub {
}
this.store.seed(entries);

ghost Sep 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve WebSocket updates received during reseeding

On initial connection or reconnect, a session frame can update the store while this REST request is in flight, after which seed(entries) replaces the entire map with the older HTTP snapshot. Because HTTP and WebSocket delivery are unordered, a session that became busy during reseeding can remain falsely idle until another activity event occurs; merge the baseline without overwriting newer frame data, or buffer/version frames received after the request starts.

AGENTS.md reference: apps/kimi-inspect/AGENTS.md:L26-L28

Useful? React with 👍 / 👎.

Comment on lines +41 to +44
function isPathSafeTaskId(taskId: string): boolean {
if (taskId.length === 0) return false;
if (taskId === '.' || taskId === '..') return false;
return !/[/\\\0]/.test(taskId);

ghost Sep 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Encode tool-call IDs before using them as filesystem paths

When a provider supplies an otherwise valid tool-call ID containing / or \, detached-task persistence rejects it here; on Windows, IDs containing characters such as : pass this check but fail in the filesystem instead. The task service swallows those persistence errors, so background task metadata and output appear to work in memory but disappear after restart. Tool-call IDs are arbitrary protocol strings, so derive an encoded filesystem key rather than requiring the external ID itself to be path-safe.

Useful? React with 👍 / 👎.

… message (#3723)

* fix(agent-core-v2): deliver cron-fired prompts as single opening turn message

isDisplayablePromptOrigin now admits cron_job/cron_missed, so the
observable turn.started carries the cron-fire prompt text instead of
leaving the opening wire user empty.

prompt.inject only dispatches turn.steer when the message actually
steers into a running turn; a fresh-turn submission is fully expressed
by turn.prompt/turn.started, so the duplicate materialization and both
opening-steer dedup hacks (live projector and coldFold) go away.

* test(agent-core-v2): drop stale compaction controller test for removed modules

The file imports #/llm/requester/machine, #/llm/message and other
modules that no longer exist after the requester-pipeline refactor; it
resurfaced in the dev rewrite and fails the suite at import time.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4b1a364a3f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

if (dir === undefined) return [];
let text: string;
try {
text = await readFile(join(dir, 'agents', agentId, 'wire.jsonl'), 'utf-8');

ghost Sep 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject path traversal in agent IDs before reading history

When searching a live session with a caller-supplied container.agent_id, searchMessagesBodySchema accepts any nonempty string and this path joins it directly beneath the agent directory. An ID such as ../../another-session/agents/main therefore reads a wire.jsonl outside the selected session and returns those messages as if they belonged to the requested container. Validate the ID against the live session roster or verify that the resolved path remains inside the session's agents directory before reading it.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant